/**
 * DLA/Oscilloscope Project Software
 * 
 * By Kevin Cuzner <kevincuzner@yahoo.com>
 * 
 * This file is part of the DLA/Oscilloscope Project Software.
 *
 * The DLA/Oscilloscope Project Software is free software: you can 
 * redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 *
 * The DLA/Oscilloscope Project Software is distributed in the hope
 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with The DLA/Oscilloscope Project Software.
 * If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;

namespace DLAOscilloscopegtk
{
	/// <summary>
	/// Defines a byte/bit mapping.
	/// The basic purpose of this class is to map bits from bytes in the SampleWindow
	/// to the bits in the channel. Maximum channel width is 64 bits.
	/// </summary>
	/// <exception cref='ArgumentException'>
	/// Is thrown when an argument passed to a method is invalid.
	/// </exception>
	/// <exception cref='ArgumentOutOfRangeException'>
	/// Is thrown when an argument passed to a method is invalid because it is outside the allowable range of values as
	/// specified by the method.
	/// </exception>
	public class ChannelMapping
	{
		protected string name; //the name of this channel mapping
		protected uint width; //the width of the channel in bits
		protected List<ChannelBitMapping> mappings; //the list of channel bit mappings
		
		/// <summary>
		/// Initializes a new instance of the <see cref="DLAOscilloscopegtk.ChannelMapping"/> class.
		/// </summary>
		/// <param name='name'>
		/// Name of the channel
		/// </param>
		/// <exception cref='ArgumentException'>
		/// Is thrown when an argument passed to a method is invalid.
		/// </exception>
		public ChannelMapping (string name)
		{
			if (name == "")
				throw new ArgumentException ("name must not be an empty string");
			
			this.name = name;
			this.width = 0; //start out as 0. As mappings are added, this will be incremented
		}
		
		/// <summary>
		/// Adds a bit mapping to the channel for the current bit number. Bits must
		/// be mapped in order from LSB to MSB
		/// </summary>
		/// <returns>
		/// The bit number mapped to in the channel
		/// </returns>
		/// <param name='sampleByteNumber'>
		/// Sample byte number to map from
		/// </param>
		/// <param name='sampleBitNumber'>
		/// Sample bit number to map from
		/// </param>
		public uint MapBit (uint sampleByteNumber, uint sampleBitNumber)
		{
			if (this.width == 64) {
				//we can no longer add mappings to this channel since it is the width of a ulong
				return 0;
			}
			
			this.mappings.Add (new ChannelBitMapping (sampleByteNumber, sampleBitNumber, this.width));
			this.width++;
			return this.width;
		}
		
		/// <summary>
		/// Returns the channel mapping for a specific bit. If the bit has not been
		/// assigned a mapping this will return null
		/// </summary>
		/// <returns>
		/// The mapping at the given bit or null
		/// </returns>
		/// <param name='bit'>
		/// Bit to get mapping for
		/// </param>
		public ChannelBitMapping GetMappingAtBit (uint bit)
		{
			//gets the mapping for a specific bit
			if (bit >= this.width)
				return null;
			
			return this.mappings [(int)bit];
		}
		
		/// <summary>
		/// Clears the channel mapping.
		/// </summary>
		public void ClearMapping ()
		{
			this.mappings.Clear (); //Note: This could be a memory leak
			this.width = 0;
		}
		
		/// <summary>
		/// Gets the channel width in bits.
		/// </summary>
		/// <value>
		/// The channel width.
		/// </value>
		public uint Width {
			get {
				return width;
			}
		}
		
		/// <summary>
		/// Gets the name of the channel mapping.
		/// </summary>
		/// <value>
		/// The channel mapping name.
		/// </value>
		public string Name {
			get {
				return name;
			}
		}
		
		/// <summary>
		/// Encapsulates a channel bit mapping.
		/// </summary>
		/// <exception cref='ArgumentOutOfRangeException'>
		/// Is thrown when an argument passed to a method is invalid because it is outside the allowable range of values as
		/// specified by the method.
		/// </exception>
		public class ChannelBitMapping
		{
			protected uint sByteN; //sample byte number
			protected uint sBitN; //sample bit number
			protected uint cBitN; //channel bit number
			
			/// <summary>
			/// Initializes a new instance of the <see cref="DLAOscilloscopegtk.ChannelMapping.ChannelBitMapping"/> class.
			/// This only is called from <see cref="DLAOscilloscopegtk.ChannelMapping.AddBit"/>
			/// </summary>
			/// <param name='sampleByteNumber'>
			/// Sample byte number.
			/// </param>
			/// <param name='sampleBitNumber'>
			/// Sample bit number.
			/// </param>
			/// <param name='channelBitNumber'>
			/// Channel bit number.
			/// </param>
			/// <exception cref='ArgumentOutOfRangeException'>
			/// Is thrown when an argument passed to a method is invalid because it is outside the allowable range of values as
			/// specified by the method.
			/// </exception>
			public ChannelBitMapping (uint sampleByteNumber, uint sampleBitNumber, uint channelBitNumber)
			{
				//check bit boundaries
				if (channelBitNumber < 0)
					throw new ArgumentOutOfRangeException ("channelBitNumber", "Channel bit less than 0");
				if (sampleBitNumber < 0)
					throw new ArgumentOutOfRangeException ("sampleBitNumber", "Sample bit less than 0");
				if (sampleBitNumber > sizeof(byte))
					throw new ArgumentOutOfRangeException("sampleBitNumber", "Sample bit greater than "+sizeof(char).ToString()+" (sizeof(byte))");
				
				this.sByteN = sampleByteNumber;
				this.sBitN = sampleBitNumber;
				this.cBitN = channelBitNumber;
			}
			
			/// <summary>
			/// Gets the sample byte number this is mapped from.
			/// </summary>
			/// <value>
			/// The sample byte number.
			/// </value>
			public uint SampleByteNumber {
				get { return this.sByteN; }
			}
			
			/// <summary>
			/// Gets the sample bit number this is mapped from.
			/// </summary>
			/// <value>
			/// The sample bit number.
			/// </value>
			public uint SampleBitNumber {
				get { return this.sBitN; }
			}
			
			/// <summary>
			/// Gets the channel bit number this is mapped to.
			/// </summary>
			/// <value>
			/// The channel bit number.
			/// </value>
			public uint ChannelBitNumber {
				get {
					return this.cBitN;
				}
			}
		}
		
		/// <summary>
		/// Read only channel mapping container which contains a list of channel mappings
		/// </summary>
		public class ChannelMappingContainer
		{
			protected List<ChannelMapping> mappings;
			
			/// <summary>
			/// Initializes a new instance of the <see cref="DLAOscilloscopegtk.ChannelMapping.ChannelMappingContainer"/> class.
			/// </summary>
			/// <param name='mappings'>
			/// Channel mappings in this container.
			/// </param>
			public ChannelMappingContainer (List<ChannelMapping> mappings)
			{
				this.mappings = mappings;
			}
			
			/// <summary>
			/// Gets the <see cref="DLAOscilloscopegtk.ChannelMapping.ChannelMappingContainer"/> at the specified index.
			/// </summary>
			/// <param name='index'>
			/// Index.
			/// </param>
			public ChannelMapping this [int index] {
				get {
					if (index<0 || index>=this.mappings.Count)
						return null;
					return this.mappings[index];
				}
			}
			
			/// <summary>
			/// Gets the size of the mapping list.
			/// </summary>
			/// <value>
			/// The size of the mapping list.
			/// </value>
			public int Count {
				get {
					return this.mappings.Count;
				}
			}
		}
	}
}

